package dbexp.utils;

/**
 * Narzedzia do modyfikacji Stringow
 * <OL>
 * <LI>Tool.<B>lower()</B> zamienia znaki na male
 * <LI>Tool.<B>upper()</B> zamienia znaki na duze
 * <LI>Tool.<B>fltu()</B> zamiania pierwszy znak na duza litere
 * <LI>Tool.<B>oltl()</B> zamienia wszystkie inne znaki poza pierwszym na male
 * litery
 * <LI>Tool.<B>trim()</B> obcina spacje
 * <LI>Tool.<B>reverse()</B> odwraca ciag znakow
 * <LI>Tool.<B>cut(int len)</B> obcina do podanej dlugosci
 * <LI>Tool.<B>normalize(int len)</B> normalizuje lancuchy do podanej dlugosci
 * wypelniajac dodatkowe miejsca spacjami
 * <LI>Tool.<B>normalize(int len, char add)</B> normalizuje lancuchy do podanej
 * dlugosci wypelniajac dodatkowe miejsca podanym znakiem
 * <LI>Tool.<B>replaceAll(String a, String b)</B> zamienia wszystkie wystapienia
 * ciagu a na ciag b
 * <LI>Tool.<B>replaceFirst(String a, String b)</B> zamienia pierwsze
 * wystapienie ciagu a na ciag b
 * <LI>Tool.<B>replaceLast(String a, String b)</B> zamienia ostatnie wystapienie
 * ciagu a na ciag b
 * <LI>Tool.<B>appendOnStart(String append)</B> dolacza podany ciag na poczatek
 * <LI>Tool.<B>appendOnEnd(String append)</B> dolacza podanz ciag na koniec
 * </OL>
 */
public abstract class Tool{
	protected String name = NONE;
	/**
	 * Numer ciagu w tablicy/vectorze
	 */
	private static Integer nr = null;
	/**
	 * Domyslny konstruktor
	 * Wszystkie wartosci posiadaja stan domyslny
	 */
	protected Tool(){}
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu oraz czy przetwarzac wszystkie wyrazy w ciagu
	 * @param e czy przetwarzac kazdy wyraz w ciagu
	 */
	protected Tool(boolean e){}
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu
	 * @param t ciag znakow
	 */
	protected Tool(String t){}
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu
	 * @param rx szukany ciag znakow
	 * @param rt zamieniany ciag znakow
	 */
	protected Tool(String rx, String rt){}
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu
	 * @param len nowa dlugosc ciagu
	 */
	protected Tool(int len){}
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu
	 * @param add znak wstawiany do normalizacji dlugosci stringow, default ' '
	 */
	protected Tool(char add){}
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu
	 * @param len nowa dlugosc ciagu
	 * @param add znak wstawiany do normalizacji dlugosci stringow, default ' '
	 */
	protected Tool(int len, char add){}
	
	/**
	 * Konstruktor definiujacy narzedzie okreslonego typu
	 * @param con kontener z opcjami
	 */
	protected Tool(Container con){}
	/**
	 * @return narzedzie do zmiany wszystkich znakow w ciagu na male litery
	 */
	public static Tool lower(){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return in.toString().toLowerCase();
			}
		}.setName(LOWER);
	}
	
	/**
	 * @return narzedzie do zmiany wszystkich znakow w ciagu na duze litery 
	 */
	public static Tool upper(){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return in.toString().toUpperCase();
			}
		}.setName(UPPER);
	}
	
	/**
	 * @return narzedzie do zmiany pierwszego znaku w ciagu na duza litere 
	 */
	public static Tool fltu(){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return (in.toString().substring(0 , 1).toUpperCase()+(in.toString().substring(1)));
			}
		}.setName(FLTU);
	}
	
	/**
	 * @param e czy przetwarzac kazdy wyraz w ciagu
	 * @return narzedzie do zmiany pierwszego znaku w kazdym wyrazie ciagu na duza litere 
	 */
	public static Tool fltu(final boolean e){
		return new Tool(e){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				String out = in.toString();
				if(e){
					String[] each = in.toString().split(" ");
					if(each.length > 0){
						for(int e = 0; e < each.length; e++){
							String orig = each[e];
							if(orig.length()>1){
								each[e] = (orig.substring(0 , 1).toUpperCase()+(orig.substring(1)));
							}
						}
						out = StringUtil.join(each," ", false);
					}
				}else{
					out = (out.substring(0 , 1).toUpperCase()+(out.substring(1)));
				}
				return out;
			}
		}.setName(FLTU_BOOLEAN);
	}
	
	/**
	 * @return narzedzie do zmiany wszystkich znakow w ciagu poza pierwszym znakiem na male litery 
	 */
	public static Tool oltl(){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return (in.toString().substring(0 , 1) + (in.toString().substring(1)).toLowerCase());
			}
		}.setName(OLTL);
	}
	
	/**
	 * @return narzedzie do usuwania bialych znakow z brzegow ciagu
	 */
	public static Tool trim(){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return in.toString().trim();
			}
		}.setName(TRIM);
	}
	
	/**
	 * @return narzedzie do usuwania dowolnych znakow z brzegow ciagu
	 */
	public static Tool trim(final String ct){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				String out = in.toString().trim();
				if(out.startsWith(ct)){
					out = out.replaceFirst("["+ct+"]", "");
				}
				if(out.endsWith(ct)){
					int lastIndex = out.lastIndexOf(ct);
					if(lastIndex != -1){
						out = out.substring(0, lastIndex)+out.substring(lastIndex).replaceFirst("["+ct+"]", "");
					}
					out = (String) Tool.replaceLast(ct, "").invoke(out);
				}
				return out.trim();
			}
		}.setName(TRIM);
	}
	/**
	 * @return narzedzie do zamiany kolejmosci znakow w ciagu
	 */
	public static Tool reverse(){
		return new Tool(){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return new StringBuffer("").append(in).reverse().toString();
			}
		}.setName(REVERSE);
	}
	
	/**
	 * W przypadku długości ujemnej ucięty zostanie ciąg od końca
	 * @param len nowa dlugosc ciagu 
	 * @return narzedzie do obcinania ciagu do wskazanej dlugosci
	 */
	public static Tool cut(final int len){
		return new Tool(len){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				String out = in.toString();
				int newLength = len;
				if(newLength>0 && newLength < in.toString().length()){
					out = new String(in.toString().substring(0, newLength));
				}else if(newLength < 0 && Math.abs(newLength) < out.length()){
					out = out.substring(out.length()+newLength);
				}
				return out;
			}
		}.setName(CUT);
	}

	/**
	 * @param len nowa dlugosc ciagu
	 * @param add znak wstawiany do normalizacji dlugosci stringow, default = ' '
	 * @return narzedzie do normalizacji dlugosci ciagu
	 */
	public static Tool normalize(final int len, final char add){
		return new Tool(len, add){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				int newLength = len;
				char[] oldString = in.toString().toCharArray();
				char[] newString = new char[Math.abs(newLength)];
				if(newLength > in.toString().length()){
					for(int c = 0 ; c < newLength; c++){
						if(c < oldString.length){
							newString[c] = oldString[c];
						}else{
							newString[c] = add;
						}
					}
				}else if(newLength < 0 && Math.abs(newLength) > in.toString().length()){
					newLength = Math.abs(newLength);
					for(int c = 0 ; c < newLength; c++){
						if(c < newLength - oldString.length){
							newString[c] = add;
						}else{
							newString[c] = oldString[c-(newLength - oldString.length)];
						}
					}
				}else{
					newString = oldString;
				}
				return new String(newString);
			}
		}.setName(NORMALIZE);
	}
	
	/**
	 * @param len nowa dlugosc ciagu
	 * @return narzedzie do normalizacji dlugosci ciagu
	 */
	public static Tool normalize(int len){
		return Tool.normalize(len, ' ');
	}
	
	/**
	 * @param regex szukany ciag
	 * @param replacement zamieniany ciag
	 * @return narzedzie do zamieniania wszystkich wystapien szukanego ciagu na zamieniany
	 */
	public static Tool replaceAll(final String regex, final String replacement){
		return new Tool(regex){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return in.toString().replaceAll(regex, replacement);
			}
		}.setName(REPLACE_ALL);
	}
	
	/**
	 * @param regex szukany ciag
	 * @param replacement zamieniany ciag
	 * @return narzedzie do zamieniania pierwszego wystapiena szukanego ciagu na zamieniany
	 */
	public static Tool replaceFirst(final String regex, final String replacement){
		return new Tool(regex, replacement){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				return in.toString().replaceFirst(regex, replacement);
			}
		}.setName(REPLACE_FIRST);
	}
	
	/**
	 * @param regex szukany ciag
	 * @param replacement zamieniany ciag
	 * @return narzedzie do zamieniania ostatniego wystapiena szukanego ciagu na zamieniany
	 */
	public static Tool replaceLast(final String regex, final String replacement){
		return new Tool(regex, replacement){
			public Object invoke(Object in) {
				if(in==null){
					return in;
				}
				String out = in.toString();
				if(out != null && regex != null && replacement!=null){
					int lastIndex = out.lastIndexOf(regex);
					if(lastIndex != -1){
						String rx = regex;
						String rt = replacement;
						out = out.substring(0, lastIndex)+out.substring(lastIndex).replaceFirst(rx, rt);
					}
				}
				return out;
			}
		}.setName(REPLACE_LAST);
	}
	
	/**
	 * @param append ciag doklejany
	 * @return narzedzie do doklejania ciagu <code>append</code> na poczatek ciagu
	 */
	public static Tool appendOnStart(final String append){
		return new Tool(append){
			public Object invoke(Object in) {
				final Integer x = nr;
				if(append!=null && append.length()>0){
					return (checkAppend(append,x)+in);
				}
				return in;
			}
		}.setName(APPEND_ON_START);
	}
	
	/**
	 * @param append ciag doklejany
	 * @return narzedzie do doklejania ciagu <code>append</code> na koniec ciagu
	 */
	public static Tool appendOnEnd(final String append){
		return new Tool(append){
			public Object invoke(Object in) {
				final Integer x = nr;
				if(append!=null && append.length()>0){
					return (in+checkAppend(append,x));
				}
				return in;
			}
		}.setName(APPEND_ON_END);
	}
	
	/**
	 * @return narzedzie do doklejania ciagu <code>append</code> na koniec ciagu
	 */
	public static Tool nothing(){
		return new Tool(){
			public Object invoke(Object in) {
				return in;
			}
		}.setName(NONE);
	}
	/**
	 * Ustawia numer elementu
	 * @param i numer
	 */
	public static void setNr(Integer i){
		nr = i;
	}
	
	/**
	 * Pobiera numer elementu
	 * @return numer
	 */
	public static Integer getNr(){
		return nr;
	}
	/**
	 * Sprawdza czy w ciagu wystepuje zmienna $nr oraz zamienia jej wystapienie numerem elementu
	 * @param a ciag wejsciowy
	 * @param x numer elementu
	 * @return ciag znakowy zamienionym wystapieniem zmiennej $nr numerem elementu
	 */
	private static String checkAppend(String a, Integer x){
		String append = a;
		if(a != null && a.length()>0){
			if(append.indexOf("$nr")!=-1 && x!=null){
				if(append.indexOf("$nr+")!=-1){
					if(append.indexOf("$nr++")!=-1){
						append = a.substring(0,a.indexOf("$nr++"))+(String.valueOf(x.intValue()+1))+a.substring(a.indexOf("$nr++")+5);
					}else{
						String appendNumber = a.substring(a.indexOf("$nr+")+4);
						char[] charsNumber = appendNumber.toCharArray();
						char[] newCharsNumber = new char[charsNumber.length];
						char[] newCharsLetter = new char[charsNumber.length];
						boolean isOneLetter = false;
						for(int c = 0, n = 0, l = 0; c < charsNumber.length; c++){
							if(Character.isDigit(charsNumber[c]) && !isOneLetter ){
								newCharsNumber[n++] = charsNumber[c];
							}else{
								newCharsLetter[l++] = charsNumber[c];
								isOneLetter = true;
							}
						}
						appendNumber = new String(newCharsNumber).trim();
						int addNumber = 0;
						try{
							addNumber = Integer.parseInt(appendNumber);
							append = a.substring(0,a.indexOf("$nr+"))+String.valueOf(x.intValue()+addNumber);
						}catch(NumberFormatException e){}
							append += new String(newCharsLetter).trim();
					}
				}else if(append.indexOf("$nr-")!=-1){
					if(append.indexOf("$nr--")!=-1){
						append = a.substring(0,a.indexOf("$nr--"))+(String.valueOf(x.intValue()-1))+a.substring(a.indexOf("$nr--")+5);
					}else{
						String appendNumber = append.substring(a.indexOf("$nr-")+4);
						char[] charsNumber = appendNumber.toCharArray();
						char[] newCharsNumber = new char[charsNumber.length];
						char[] newCharsLetter = new char[charsNumber.length];
						boolean isOneLetter = false;
						for(int c = 0, n = 0, l = 0; c < charsNumber.length; c++){
							if(Character.isDigit(charsNumber[c]) && !isOneLetter ){
								newCharsNumber[n++] = charsNumber[c];
							}else{
								newCharsLetter[l++] = charsNumber[c];
								isOneLetter = true;
							}
						}
						appendNumber = new String(newCharsNumber).trim();
						int addNumber = 0;
						try{
							addNumber = Integer.parseInt(appendNumber);
							append = a.substring(0,a.indexOf("$nr-"))+String.valueOf(x.intValue()-addNumber);
						}catch(Exception e){}
							append += new String(newCharsLetter).trim();
					}
				}else{
					append = x.toString();
				}
			}
		}
		return append;
	}
	public final boolean equals(Object obj) {
		if(!(obj instanceof Tool)){
			return false;
		}
		return getName().equals(((Tool)obj).getName());
	}
	public final int hashCode() {
		return getName().hashCode();
	}
	/**
	 * Metoda wykonawcza narzędzia
	 * @param in ciąg do przetworzenia
	 * @return przetworzony ciąg
	 */
	public abstract Object invoke(Object in);
	
	/**
	 * Metoda pobierająca nazwę narzędzia
	 * @return nazwa narzędzia
	 */
	public final String getName(){return NONE;};

	/**
	 * Jednorazowo ustawia nazwę narzędzia.<br>
	 * Bez użycia tej metody narzędzie będzie posiadało nazwę NONE
	 * @param newName nowa nazwa narzędzia
	 * @return ten objekt
	 */
	public final Tool setName(final String newName) {
		if (this.name.equals(NONE))
			this.name = newName;
		return this;
	};

	public final String toString() {
		return getName();
	}
	
	protected static final String NONE = "NONE";
	protected static final String APPEND_ON_END = "appendOnEnd";
	protected static final String APPEND_ON_START = "appendOnStart";
	protected static final String CHECK_APPEND = "checkAppend";
	protected static final String CUT = "cut";
	protected static final String FLTU = "fltu";
	protected static final String FLTU_BOOLEAN = "fltu_boolean";
	protected static final String LOWER = "lower";
	protected static final String NORMALIZE = "normalize";
	protected static final String OLTL = "oltl";
	protected static final String REPLACE_ALL = "replaceAll";
	protected static final String REPLACE_FIRST = "replaceFirst";
	protected static final String REPLACE_LAST = "replaceLast";
	protected static final String REVERSE = "reverse";
	protected static final String TRIM = "trim";
	protected static final String UPPER = "upper";
	
}
